/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	
	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

****/
package org.activequant.core.domainmodel.data;

import static org.activequant.util.tools.IdentityUtils.equalsTo;
import static org.activequant.util.tools.IdentityUtils.safeCompare;
import static org.activequant.util.tools.IdentityUtils.safeHashCode;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.types.TimeStamp;
import org.activequant.core.util.TimeStampFormat;

/**
 * Represents a single price move of the market.<br/>
 * A MarketDataEntity subclass. Holds the following associated variable:
 * <ul>
 * <li>value(double)</li>
 * </ul>
 * And the following inherited variables:
 * <ul>
 * <li>id(Long)</li>
 * <li>instrumentSpecification(InstrumentSpecification)</li>
 * <li>timeStamp(TimeStamp)</li>
 * </ul>
 * <br>
 * <em>IMPORTANT</em>: This object has "value" identity. 
 * It means that its identity
 * (hashCode, equals, and compareTo) is determined by subset of the fields,
 * declared "business keys". Other fields are considered a "payload".
 * This identity reflects the suggested unique key constraints in the
 * related sql table.
 * <em>IMPORTANT</em>: If this object is added to a collection that relies on
 * identity, changing business key will break the collection 
 * contract and will cause unexpected results.
 * 
 */
public class TimedValue extends MarketDataEntity<TimedValue> {
	
	private static final long serialVersionUID = 7789600340466925266L;

	/**
     * contains the value we track.<br/>
     * private double value = NOT_SET;
     */
    private double value = NOT_SET;
    
    /**
     * just a very flat constructor.<br/>
     * An empty TimedValue(MarketDataEntity subclass) constructor.
     *
     */
    public TimedValue() {
    	
    }
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given date(TimeStamp) to set its associated/inherited timeStamp(TimeStamp).
     * @param date
     */
    public TimedValue(TimeStamp date) {
    	super(date);
    }
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given spec(InstrumentSpecification) to set its associated/inherited instrumentSpecification(InstrumentSpecification)
     * @param spec
     */
    public TimedValue(InstrumentSpecification spec) {
    	super(spec);
    }
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given spec(InstrumentSpecification) and date(TimeStamp) to set its associated/inherited 
     * instrumentSpecification(InstrumentSpecification) and timeStamp(TimeStamp)
     * @param spec
     * @param date
     */
    public TimedValue(InstrumentSpecification spec, TimeStamp date) {
    	this(date);
    	setInstrumentSpecification(spec);
	}
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given spec(InstrumentSpecification) and value(double) to set its associated/inherited 
     * instrumentSpecification(InstrumentSpecification) and value(double)
     * @param spec
     * @param value
     */
    public TimedValue(InstrumentSpecification spec, double value) {
    	super(spec);
    	this.value = value;  
    }
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given spec(InstrumentSpecification), date(TimeStamp) and value(double) to set its associated/inherited 
     * instrumentSpecification(InstrumentSpecification), timeStamp(TimeStamp) and value(double)   
     * @param spec
     * @param date
     * @param value
     */
    public TimedValue(InstrumentSpecification spec, TimeStamp date, double value) {
    	this(date);
    	setInstrumentSpecification(spec);
    	setValue(value);
    }
    
    
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given tick(double) to set its associated value(double)
     * @param priceIndication
     *
     */
    public TimedValue(double tick) {
    	this.value = tick;    	
    }
    /**
     * constructs a TimedValue(MarketDataEntity subclass) using the given date(TimeStamp) and tick(double) to set its associated timeStamp(TimeStamp) and value(double)
     * @param date
     * @param tick
     */
    public TimedValue(TimeStamp date, double tick) {
    	super(date);
    	this.value = tick;
    }
    
    
    /**
     * return object's state as string.<br/>
     * returns a String in the form: "${className}{spec=${instrumentSpecification}, timeStamp=${formattedTimeStamp}("${timeStamp}L), value=${value}}"
     * @return text
     */
    @Override
	public String toString(){
    	TimeStampFormat sdf = new TimeStampFormat("dd.MM.yyyy HH:mm:ss.SSS");
    	return getClass().getSimpleName() 
    		+ "{spec=" + getInstrumentSpecification() 
    		+ ", timeStamp=" + sdf.format(getTimeStamp()) + "(" + getTimeStamp() + "L)" 
    		+ ", value=" + getValue()
    		+ "}";
    }

    /**
     * clone this TimedValue(MarketDataEntity subclass) instance.<br/>
     * returns a new TimedValue with the same instrumentSpecification(InstrumentSpecification), timeStamp(TimeStamp) and value(double) as this TimedValue instance
     * @return cloned object
     */
    @Override
	public TimedValue clone() {
		return new TimedValue(getInstrumentSpecification(), getTimeStamp(),value);
	}

    /**
     * {@inheritDoc}
     */
	public int compareTo(TimedValue other) {
		// ATTENTION: keep in sync with hashCode();
		int rc;
		
		if((rc = safeCompare(this.getInstrumentSpecification(), 
				other.getInstrumentSpecification())) != 0) {
			return rc;
		}
		if((rc = safeCompare(this.getTimeStamp(), other.getTimeStamp())) != 0) {
			return rc;
		}
		if((rc = safeCompare(this.value, other.value)) != 0) {
			return rc;
		}
		
		return 0;
	}
	
    /**
     * {@inheritDoc}
     */
    @Override
	public int hashCode() {
		// ATTENTION: keep in sync with compareTo();
        return safeHashCode(this.getInstrumentSpecification())
        	+ safeHashCode(this.getTimeStamp())
        	+ safeHashCode(this.value);
	}
	
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object other) {
    	return equalsTo(this, other);
	}
    /**
     * returns the associated value(double)
     * @return
     */
	public double getValue() {
		return value;
	}
	/**
	 * sets the value.<br/>
	 * sets the associated value(double) with the given tradedPrice(double)
	 * @param value. 
	 */
	public void setValue(double tradedPrice) {
		this.value = tradedPrice;
	}
	/**
	 * returns a double[] array holding the associated value(double)
	 * @return
	 */
	public double[] getDoubles() {
		return new double[] { value };
	}
	/**
	 * sets the associated value(double) with the given values(double[])
	 * @param values
	 */
	public void setDoubles(double[] values) {
		assert(values.length == 1) : "values.length != 1)";
		setValue(values[0]);
	}
}